﻿using System;
using UnityEngine;

public class ScrollCenterChecker<T> : IScrollChecker, IScrollSkipUpdater
{
    public int centerIndex;
    public float smoothTime;
    public Action onStopHandler;
    public Action<int> onCenterHandler;

    float current;
    float target;
    float currentVelocity;

    public ScrollList<T> scrollList;

    private int scrollCenterIndex = -1;
    private float maxSpeed;
    private float defaultMaxSpeed = 5000;
    private float minSpeed = 1f;
    private bool isForbitReverse;

    public bool IsNeedSkip { get; private set; }

    public ScrollCenterChecker(int centerIndex, float smoothTime, bool isForbitReverse,
        Action onStopHandler, Action<int> onCenterHandler, ScrollList<T> scrollList)
    {
        this.centerIndex = centerIndex;
        this.smoothTime = smoothTime;
        this.onStopHandler = onStopHandler;
        this.onCenterHandler = onCenterHandler;
        this.scrollList = scrollList;
        this.isForbitReverse = isForbitReverse;
    }

    public bool Check()
    {
        IsNeedSkip = false;
        if (maxSpeed == 0)
            CheckStartSpeed();

        if (maxSpeed == 0)
            return false;

        IsNeedSkip = true;
        if (CheckUpdatePosition())
            return true;

        return false;
    }

    public void CheckStartSpeed()
    {
        var speed = GetCurrentStartMoveSpeed();

        if (scrollList.ScrollRectEx.horizontal && Mathf.Abs(scrollList.ScrollRectEx.Velocity.x) <= minSpeed)
        {
            maxSpeed = defaultMaxSpeed;
            return;
        }

        if (scrollList.ScrollRectEx.vertical && Mathf.Abs(scrollList.ScrollRectEx.Velocity.y) <= minSpeed)
        {
            maxSpeed = defaultMaxSpeed;
            return;
        }

        if (scrollList.ScrollRectEx.horizontal && Mathf.Abs(scrollList.ScrollRectEx.Velocity.x) <= Mathf.Abs(speed))
        {
            maxSpeed = scrollList.ScrollRectEx.Velocity.x;
            return;
        }

        if (scrollList.ScrollRectEx.vertical && Mathf.Abs(scrollList.ScrollRectEx.Velocity.y) <= Mathf.Abs(speed))
        {
            maxSpeed = scrollList.ScrollRectEx.Velocity.y;
            return;
        }
    }

    // 当前时刻计算得到的速度，是否小于当前正在滚动的速度，为了使得效果更平滑，要小于当前滚动速度在接管滚动逻辑
    public float GetCurrentStartMoveSpeed()
    {
        float perItem = 0;
        float spacing = 0;
        float centerOffset = 0;

        var matrix = scrollList.ScrollRectEx.viewRect.worldToLocalMatrix;
        scrollList.Creator.TryGetItemByChildIndex(0, out var item);

        if (scrollList.ScrollRectEx.horizontal)
        {
            perItem = scrollList.ScrollRectEx.defaultItem.GetComponent<RectTransform>().sizeDelta.x;
            spacing = scrollList.Creator.Spacing.x;
            centerOffset = matrix.MultiplyPoint3x4(item.position).x;
        }
        else
        {
            perItem = scrollList.ScrollRectEx.defaultItem.GetComponent<RectTransform>().sizeDelta.y;
            spacing = scrollList.Creator.Spacing.y;
            centerOffset = -matrix.MultiplyPoint3x4(item.position).y;
        }

        var indexOffset = scrollList.IndexDataTool.GetChildIndex(centerIndex);
        target = indexOffset * (perItem + spacing) + centerOffset;

        // 如果是循环，且不能反向
        if (scrollList.isLoopData && isForbitReverse && target < 0)
        {
            indexOffset += scrollList.Count;
            target = indexOffset * (perItem + spacing) + centerOffset;
        }

        Mathf.SmoothDamp(current, target, ref currentVelocity, smoothTime);
        return currentVelocity;
    }

    public bool CheckUpdatePosition()
    {
        float vlaue = Mathf.SmoothDamp(current, target, ref currentVelocity, smoothTime, maxSpeed);
        var offset = current - vlaue;
        current = vlaue;
        scrollList.ScrollRectEx.MoveContent(new Vector2(offset, -offset));

        CheckCenter();

        if (Mathf.Abs(currentVelocity) <= minSpeed)
        {
            scrollList.ScrollRectEx.StopMove();
            onStopHandler?.Invoke();
            return true;
        }

        return false;
    }

    public void CheckCenter()
    {
        if (onCenterHandler == null)
            return;

        scrollList.IndexDataTool.ForFirstToLast((dataIndex, childIndex) =>
        {
            var item = scrollList.Creator.activeList[childIndex];
            var bounds = scrollList.ScrollRectEx.GetBounds(item);

            var isBreak = false;

            if (scrollList.ScrollRectEx.horizontal)
            {
                if (bounds.min.x < 0 && bounds.max.x > 0)
                    isBreak = true;
            }
            else
            {
                if (bounds.min.y < 0 && bounds.max.y > 0)
                    isBreak = true;
            }

            if (isBreak)
            {
                if (dataIndex != scrollCenterIndex)
                    onCenterHandler(dataIndex);
                scrollCenterIndex = dataIndex;
            }

            return isBreak;
        });

    }
}